package com.example.jobtask.util;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import javax.tools.*;
import java.io.*;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.file.*;
import java.util.Arrays;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 动态类加载器，支持热插拔式任务
 */
@Component
public class DynamicClassLoader {
    
    private static final Logger logger = LoggerFactory.getLogger(DynamicClassLoader.class);
    
    // 缓存已加载的类
    private final ConcurrentHashMap<String, Class<?>> classCache = new ConcurrentHashMap<>();
    
    // 热插拔任务目录
    private final String HOTPLUG_DIR = System.getProperty("user.dir") + "/hotplug-tasks";
    
    // 编译输出目录
    private final String COMPILE_OUTPUT_DIR = System.getProperty("user.dir") + "/compiled-tasks";
    
    public DynamicClassLoader() {
        // 创建必要的目录
        createDirectories();
    }
    
    /**
     * 创建必要的目录
     */
    private void createDirectories() {
        try {
            Files.createDirectories(Paths.get(HOTPLUG_DIR));
            Files.createDirectories(Paths.get(COMPILE_OUTPUT_DIR));
            logger.info("创建热插拔目录: {}", HOTPLUG_DIR);
            logger.info("创建编译输出目录: {}", COMPILE_OUTPUT_DIR);
        } catch (IOException e) {
            logger.error("创建目录失败", e);
        }
    }
    
    /**
     * 动态编译Java文件
     * @param javaFilePath Java文件路径
     * @return 编译是否成功
     */
    public boolean compileJavaFile(String javaFilePath) {
        try {
            // 获取Java编译器
            JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
            if (compiler == null) {
                logger.error("无法获取Java编译器，请确保使用JDK而不是JRE");
                return false;
            }
            
            // 设置编译选项
            StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, null, null);
            
            // 设置编译输出目录
            fileManager.setLocation(StandardLocation.CLASS_OUTPUT, Arrays.asList(new File(COMPILE_OUTPUT_DIR)));
            
            // 获取要编译的文件
            Iterable<? extends JavaFileObject> compilationUnits = 
                fileManager.getJavaFileObjectsFromFiles(Arrays.asList(new File(javaFilePath)));
            
            // 设置编译选项
            Iterable<String> options = Arrays.asList("-d", COMPILE_OUTPUT_DIR);
            
            // 执行编译
            JavaCompiler.CompilationTask task = compiler.getTask(null, fileManager, null, options, null, compilationUnits);
            boolean success = task.call();
            
            fileManager.close();
            
            if (success) {
                logger.info("成功编译Java文件: {}", javaFilePath);
                return true;
            } else {
                logger.error("编译Java文件失败: {}", javaFilePath);
                return false;
            }
            
        } catch (Exception e) {
            logger.error("编译Java文件时发生异常: " + javaFilePath, e);
            return false;
        }
    }
    
    /**
     * 从Java文件路径动态加载类
     * @param javaFilePath Java文件路径
     * @param className 类名（包含包路径）
     * @return 加载的类
     */
    public Class<?> loadClassFromFile(String javaFilePath, String className) {
        try {
            // 首先检查缓存
            Class<?> cachedClass = classCache.get(className);
            if (cachedClass != null) {
                logger.info("从缓存中获取类: {}", className);
                return cachedClass;
            }
            
            // 编译Java文件
            if (!compileJavaFile(javaFilePath)) {
                throw new RuntimeException("编译Java文件失败: " + javaFilePath);
            }
            
            // 创建类加载器
            URL[] urls = {new File(COMPILE_OUTPUT_DIR).toURI().toURL()};
            URLClassLoader classLoader = new URLClassLoader(urls, this.getClass().getClassLoader());
            
            // 加载类
            Class<?> clazz = classLoader.loadClass(className);
            
            // 缓存类
            classCache.put(className, clazz);
            
            logger.info("成功加载类: {}", className);
            return clazz;
            
        } catch (Exception e) {
            logger.error("加载类失败: " + className, e);
            throw new RuntimeException("加载类失败: " + className, e);
        }
    }
    
    /**
     * 直接加载已编译的类
     * @param className 类名（包含包路径）
     * @return 加载的类
     */
    public Class<?> loadCompiledClass(String className) {
        try {
            // 首先检查缓存
            Class<?> cachedClass = classCache.get(className);
            if (cachedClass != null) {
                logger.info("从缓存中获取类: {}", className);
                return cachedClass;
            }
            
            // 创建类加载器
            URL[] urls = {new File(COMPILE_OUTPUT_DIR).toURI().toURL()};
            URLClassLoader classLoader = new URLClassLoader(urls, this.getClass().getClassLoader());
            
            // 加载类
            Class<?> clazz = classLoader.loadClass(className);
            
            // 缓存类
            classCache.put(className, clazz);
            
            logger.info("成功加载已编译的类: {}", className);
            return clazz;
            
        } catch (Exception e) {
            logger.error("加载已编译的类失败: " + className, e);
            throw new RuntimeException("加载类失败: " + className, e);
        }
    }
    
    /**
     * 清除类缓存
     * @param className 类名
     */
    public void clearClassCache(String className) {
        classCache.remove(className);
        logger.info("清除类缓存: {}", className);
    }
    
    /**
     * 清除所有类缓存
     */
    public void clearAllClassCache() {
        classCache.clear();
        logger.info("清除所有类缓存");
    }
    
    /**
     * 获取热插拔目录路径
     */
    public String getHotplugDir() {
        return HOTPLUG_DIR;
    }
    
    /**
     * 获取编译输出目录路径
     */
    public String getCompileOutputDir() {
        return COMPILE_OUTPUT_DIR;
    }
    
    /**
     * 检查Java文件是否存在
     */
    public boolean javaFileExists(String javaFilePath) {
        return Files.exists(Paths.get(javaFilePath));
    }
}